#include <iostream>

#include "smppstack.h"
#include "linkinfo.h"
#include "cmd5.h"
#include <strstream>
#include <QDateTime>
#include <QTextCodec>
#include <QTextStream>
#include <QMessageBox>

char itoabuff[256];
char* itoa(Smpp::Uint16 i)
{
	memset(itoabuff,'\0',256);
	sprintf(itoabuff,"%d",i);
	return itoabuff;
}

BaseTestClass::BaseTestClass()
{

}

BaseTestClass::~BaseTestClass()
{}


TestSender::~TestSender()
{}


void TestSender::result()
{

}

TestReceiver::~TestReceiver()
{}

void TestReceiver::result()
{

}
SmppBase::SmppBase(ROLE actor,QObject * parent) 
	: QObject(parent)
{
	mActor=actor;
	
	mTempStr[0]=0;

//	mTurnRead=true;
	mStatus=Init;
	mBaseSeq=0;
//	mBaseMsgId=0;

//	mLiveCounterR=0;
	mLiveCounterT=0;

	mTcpServer = new QTcpServer(this);

	mTimer_EnquireLink = new QTimer(this);
	connect(mTimer_EnquireLink, SIGNAL(timeout()), this, SLOT(doEnquireLink()));

//	delayTimer=new QTimer(this);
//	connect(delayTimer, SIGNAL(timeout()), this, SLOT(delaysend()));

}

SmppBase::~SmppBase()
{
//	mTcpSocketRecv->abort();
//	delete mTcpSocketRecv;

	mTcpSocketTrans->abort();
	delete mTcpSocketTrans;

	mTcpServer->close();
	delete mTcpServer;

	mTimer_Bind->stop();
	delete mTimer_Bind;

	mTimer_EnquireLink->stop();
	delete mTimer_EnquireLink;

//	delayTimer->stop();
//	delete delayTimer;
}

void SmppBase::process()
{

}

void SmppBase::sendmsg(QByteArray buff)
{
	
}




void SmppBase::list()
{
	
}

void SmppBase::doEnquireLink()
{
	std::cout<<"doEnquireLink\n";
	if (Active==mStatus)
	{
		send_enquire_link();

		mLiveCounterT--;

		if (0>=mLiveCounterT)
		{
//			mTcpSocketTrans->abort();
			mStatus = Init;
			emit breakup();
		}
	}
}

    // function that reads an SMPP PDU from a socket.
    // The contents are placed in a std::vector which is returned
bool SmppBase::read_smpp_pdu(Smpp::Uint32& command_id) {
    // read the header
    QDataStream &ds = mDataStreamTrans;
	
    mRcvBuffer.resize(16);
	
    int n = ds.readRawData((char*)&mRcvBuffer[0], 16);
	if (n<=0)
		return false;

    // extract the length from the header
    // Smpp::get_command_length() is an auxiliary function defined in
    // aux_types.hpp used for extracting the command_length from an
    // encoded PDU.
    // There are similar functions for the other header parameters.
    Smpp::Uint32 command_len = Smpp::get_command_length((Smpp::Uint8*)&mRcvBuffer[0]);

	command_id = Smpp::get_command_id((Smpp::Uint8*)&mRcvBuffer[0]);

    // read the remainder of the PDU.
    // Some PDUs (e.g. enquire_link) only contain a header (16 octets)
    // hence the condition.
    if(command_len > 16) {
        mRcvBuffer.resize(command_len); // resize to the number of octets.
        n = ds.readRawData((char*)&mRcvBuffer[16], command_len-16);
    }

	std::cout<<"Recv Msg: len="<<n<<" command_id="<<command_id<<"\n";
//    Smpp::chex_dump(&mRcvBuffer[0], mRcvBuffer.size(), stdout);

    return true;
}
	
bool SmppBase::valid(Smpp::Uint32 command_id, bool isRcvChannel)
{
	return true;
/*	if (Client==mActor)
	{
		switch (command_id)
		{
	//		case Smpp::CommandId::BindTransceiverResp:
	//			return !isRcvChannel;
			case Smpp::CommandId::BindTransmitterResp:
				return !isRcvChannel;
			case Smpp::CommandId::BindReceiverResp:
				return isRcvChannel;
			case Smpp::CommandId::EnquireLink:
				return true;
			case Smpp::CommandId::EnquireLinkResp:
				return true;
			case Smpp::CommandId::SubmitSmResp:
				return !isRcvChannel;
	//		case Smpp::CommandId::DataSmResp:
	//			return !isRcvChannel;
			case Smpp::CommandId::DeliverSm:
				return isRcvChannel;
			case Smpp::CommandId::UnbindResp:
				return true;
			default:
				return false;
		}
	}else
		{
			switch (command_id)
			{
		//		case Smpp::CommandId::BindTransceiverResp:
		//			return !isRcvChannel;
				case Smpp::CommandId::BindTransmitter:
					return isRcvChannel;
				case Smpp::CommandId::BindReceiver:
					return !isRcvChannel;
				case Smpp::CommandId::EnquireLink:
					return true;
				case Smpp::CommandId::EnquireLinkResp:
					return true;
				case Smpp::CommandId::SubmitSm:
					return isRcvChannel;
		//		case Smpp::CommandId::DataSmResp:
		//			return !isRcvChannel;
				case Smpp::CommandId::DeliverSmResp:
					return !isRcvChannel;
				case Smpp::CommandId::UnbindResp:
					return true;
				default:
					return false;
			}

		}*/
}

void SmppBase::send_enquire_link() {
    Smpp::EnquireLinkReq pdu(++mBaseSeq); // set the sequence number
    std::cout << "\nSending an enquire link\n";
    Smpp::Uint8* buff = (Smpp::Uint8*)pdu.encode();
    Smpp::hex_dump(buff , pdu.command_length(), std::cout);
    int bytesSent;
//	bytesSent = mDataStreamRecv.writeRawData((const char*)pdu.encode(), pdu.command_length());
	bytesSent = mDataStreamTrans.writeRawData((const char*)pdu.encode(), pdu.command_length());
}

// receive the enquire link response
void SmppBase::read_enquire_link_resp(Buffer &buf) {
    std::cout << "\nRead an enquire link repsonse\n";
    Smpp::chex_dump(&buf[0], buf.size(), stdout);

    Smpp::EnquireLinkRsp pdu;
    pdu.decode(&buf[0]);
	
	mLiveCounterT = LinkInfo::instance()->enquirelink_counter;
}

// receive the enquire link response
void SmppBase::read_enquire_link(Buffer &buf) {
    std::cout << "\nRead an enquire link\n";
    Smpp::chex_dump(&buf[0], buf.size(), stdout);

    Smpp::EnquireLinkReq pdu;
    pdu.decode(&buf[0]);

	send_enquire_link_resp(Smpp::CommandStatus::MSC_ROK, pdu.sequence_number());
}

// send an enquire link
void SmppBase::send_enquire_link_resp(Smpp::CommandStatus status, Smpp::Uint32 seqnum) {
    Smpp::EnquireLinkRsp pdu(Smpp::CommandStatus::MSC_ROK,seqnum); // set the sequence number
    std::cout << "\nSending an enquire link repsonse\n";
    Smpp::Uint8* buff = (Smpp::Uint8*)pdu.encode();
    Smpp::hex_dump(buff , pdu.command_length(), std::cout);
    int bytesSent;
	bytesSent = mDataStreamTrans.writeRawData((const char*)pdu.encode(), pdu.command_length());
}






SmppClient::SmppClient(ROLE actor,QObject * parent):SmppBase(actor,parent)
{
	mTcpSocketTrans= new QTcpSocket(this);
	mDataStreamTrans.setDevice(mTcpSocketTrans);
	connect(mTcpSocketTrans, SIGNAL(disconnected()), this, SIGNAL(breakup()));

	mTimer_Bind = new QTimer(this);
	connect(mTimer_Bind, SIGNAL(timeout()), this, SLOT(doBind()));
	mTimer_Bind->start(LinkInfo::instance()->interval_bind);
}

SmppClient::~SmppClient()
{
}

void SmppClient::doBind()
{
//	QMessageBox::critical(0, tr("doBind"),tr("doBind"));
	if (mStatus!=Active)
	{
		mTcpSocketTrans->abort();
		mTcpSocketTrans->connectToHost(LinkInfo::instance()->remoteipaddr, LinkInfo::instance()->remoteport);
		send_BindReq();
	}
	
}

void SmppClient::sendmsg(QByteArray buff)
{
	if ((mStatus!=Active)||(buff.isEmpty()))
		return;
	
    int bytesSent;
	bytesSent = mDataStreamTrans.writeRawData(buff.data(), buff.length());
}

void SmppClient::process()
{
	Smpp::Uint32 command_id = 0;
    while (read_smpp_pdu(command_id))
    {
		switch(command_id)
		{
			case Smpp::CommandId::Bind_rsp:
				std::cout<<"receive BindRsp\n";
				read_BindRsp(mRcvBuffer);
				break;
			case Smpp::CommandId::Enquire_link_req:
				std::cout<<"receive EnquireLinkReq\n";
				read_enquire_link(mRcvBuffer);
				break;
			case Smpp::CommandId::Enquire_link_rsp:
				std::cout<<"receive EnquireLinkRsp\n";
				read_enquire_link_resp(mRcvBuffer);
				break;
			case Smpp::CommandId::UnBind_rsp:
				std::cout<<"receive UnbindRsp\n";
				read_UnbindRsp(mRcvBuffer);
				break;
			default:
				QByteArray buff((char*)&mRcvBuffer[0], mRcvBuffer.size());
				//buff.append(&mRcvBuffer[0], mRcvBuffer.size());
				emit recv(command_id,buff);
				break;
		}
    }

}

void SmppClient::send_BindReq() {
    Smpp::BindReq pdu;

	unsigned int currenttime = QDateTime::currentDateTime().toTime_t();
	
	pdu.SystemID = LinkInfo::instance()->Username.toInt();
//	pdu.Password= LinkInfo::instance()->Password.c_str();
	pdu.BindMode= LinkInfo::instance()->BindMode;
	pdu.TimeStamp =  currenttime;
	pdu.ClientVersion= LinkInfo::instance()->ClientVersion;
	pdu.sequence_number(++mBaseSeq);

	char  authenticator[100];
	memset(authenticator,0,sizeof(authenticator));
	sprintf(authenticator,"%s",(char*)LinkInfo::instance()->Username.toLocal8Bit().data());
	strcat(authenticator,(char*)LinkInfo::instance()->Password.toLocal8Bit().data());
	char timestampstr[32];
	sprintf(timestampstr,"%u",currenttime);
	strcat(authenticator,timestampstr);

	CMD5 md5;
	char md5authen[33];
	memset(md5authen,0,sizeof(md5authen));
    sprintf(md5authen,"%s",(char*)md5.GetDigest((unsigned char*)authenticator, strlen(authenticator), 1));
	pdu.Password = md5authen;

	//QMessageBox::critical(0, tr("doBind"),tr("authenticator:%1\nmd5authen:%2").arg(authenticator).arg(md5authen));
	
    std::cout << "\nsend_BindReq\n";

    Smpp::Uint8* d = (Smpp::Uint8*)pdu.encode();
    Smpp::hex_dump(d , pdu.command_length(), std::cout);

    int bytesSent;
	bytesSent = mDataStreamTrans.writeRawData((const char*)pdu.encode(), pdu.command_length());
}

void SmppClient::read_BindRsp(Buffer &buf) {
    std::cout << "\nread_BindRsp\n";
    Smpp::chex_dump(&buf[0], buf.size(), stdout);

    Smpp::BindRsp pdu;
    pdu.decode(&buf[0]);

	if (pdu.command_status()==Smpp::CommandStatus::MSC_ROK)
	{
		mStatus=Active;
		mLiveCounterT=LinkInfo::instance()->enquirelink_counter;
		mTimer_Bind->stop();
		mTimer_EnquireLink->start(LinkInfo::instance()->interval_enquire_link);
		emit active();
	}
}

void SmppClient::send_UnbindReq() {
    Smpp::UnBindReq pdu;
    pdu.sequence_number(++mBaseSeq);
	
    std::cout << "\nsend_UnbindReq\n";
    Smpp::Uint8* d = (Smpp::Uint8*)pdu.encode();
    Smpp::chex_dump(d , pdu.command_length(), stdout);

    int bytesSent;
	bytesSent = mDataStreamTrans.writeRawData((const char*)pdu.encode(), pdu.command_length());
}

// receive the submit sm response
void SmppClient::read_UnbindRsp(Buffer & buf){
    std::cout << "\nread_UnbindRsp\n";
    Smpp::hex_dump(&buf[0], buf.size(), std::cout);
	
    Smpp::UnBindRsp pdu;
    pdu.decode(&buf[0]);

	QString msg;
	msg+="================UnbindRsp================";
	//emit recvmsg(msg);
}




SmppServer::SmppServer(ROLE actor,QObject * parent):SmppBase(actor,parent)
{
	mTcpSocketTrans= new QTcpSocket(this);
	mDataStreamTrans.setDevice(mTcpSocketTrans);
	connect(mTcpSocketTrans, SIGNAL(disconnected()), this, SIGNAL(breakup()));

	mTimer_Bind = new QTimer(this);
	connect(mTimer_Bind, SIGNAL(timeout()), this, SLOT(waitBind()));
	mTimer_Bind->start(LinkInfo::instance()->interval_wait_bind);
	//connect(mTcpServer, SIGNAL(newConnection()), this, SLOT(waitBind()));
	
	if (!mTcpServer->listen(QHostAddress::Any, LinkInfo::instance()->localport))
	{
		QMessageBox::critical(0, tr("Socket Error"),
										  tr("Unable to start the server: %1.")
										  .arg(mTcpServer->errorString()));
	}

	
}

SmppServer::~SmppServer()
{
}

void SmppServer::waitBind()
{
	if (mStatus!=Active)
	{
		while (mTcpServer->hasPendingConnections())
		{
			QTcpSocket *clientConnection = mTcpServer->nextPendingConnection();
			
			mRcvBuffer.resize(16);
			int n = clientConnection->read((char*)&mRcvBuffer[0], 16);
			if (n<=0)
				return ;
			
			Smpp::Uint32 command_len = Smpp::get_command_length((Smpp::Uint8*)&mRcvBuffer[0]);
			
			Smpp::Uint32 command_id = Smpp::get_command_id((Smpp::Uint8*)&mRcvBuffer[0]);
			
			// read the remainder of the PDU.
			// Some PDUs (e.g. enquire_link) only contain a header (16 octets)
			// hence the condition.
			if(command_len > 16) {
				mRcvBuffer.resize(command_len); // resize to the number of octets.
				n = clientConnection->read((char*)&mRcvBuffer[16], command_len-16);
			}
			
			std::cout<<"Recv Msg: len="<<n<<" command_id="<<command_id<<"\n";
			
			if (Smpp::CommandId::Bind_req==command_id)
				{
					delete mTcpSocketTrans;
					mTcpSocketTrans=clientConnection;
					mDataStreamTrans.setDevice(clientConnection);
					connect(mTcpSocketTrans, SIGNAL(disconnected()), this, SIGNAL(breakup()));
					read_BindReq(mRcvBuffer);
				}else
				  	{
				  		delete clientConnection;
						//omit, do nothing;
				  	}
		}
	}
}

void SmppServer::sendmsg(QByteArray buff)
{
	if ((mStatus!=Active)||(buff.isEmpty()))
		return;
	
    int bytesSent;
	bytesSent = mDataStreamTrans.writeRawData(buff.data(), buff.length());
}

void SmppServer::process()
{
	Smpp::Uint32 command_id = 0;
    while (read_smpp_pdu(command_id))
    {
		switch(command_id)
		{
			case Smpp::CommandId::Bind_req:
				std::cout<<"receive BindReq\n";
				read_BindReq(mRcvBuffer);
				break;
			case Smpp::CommandId::Enquire_link_req:
				std::cout<<"receive EnquireLinkReq\n";
				read_enquire_link(mRcvBuffer);
				break;
			case Smpp::CommandId::Enquire_link_rsp:
				std::cout<<"receive EnquireLinkRsp\n";
				read_enquire_link_resp(mRcvBuffer);
				break;
			case Smpp::CommandId::UnBind_rsp:
				std::cout<<"receive UnbindRsp\n";
				read_UnbindRsp(mRcvBuffer);
				break;
			default:
				QByteArray buff((char*)&mRcvBuffer[0], mRcvBuffer.size());
				//buff.append(&mRcvBuffer[0], mRcvBuffer.size());
				emit recv(command_id,buff);
				break;
		}
    }

}

void SmppServer::read_BindReq(Buffer &buf) {
    std::cout << "\nRead a BindReq\n";
    Smpp::chex_dump(&buf[0], buf.size(), stdout);

    Smpp::BindReq pdu;
    pdu.decode(&buf[0]);

	char  authenticator[100];
	memset(authenticator,0,sizeof(authenticator));
	sprintf(authenticator,"%s",(char*)LinkInfo::instance()->Username.toLocal8Bit().data());
	strcat(authenticator,(char*)LinkInfo::instance()->Password.toLocal8Bit().data());
	char timestampstr[32];
	sprintf(timestampstr,"%u",pdu.TimeStamp.value());
	strcat(authenticator,timestampstr);

	CMD5 md5;
	char md5authen[33];
	memset(md5authen,0,sizeof(md5authen));
    sprintf(md5authen,"%s",(char*)md5.GetDigest((unsigned char*)authenticator, strlen(authenticator), 1));
	

	if (0==strncmp(md5authen, pdu.Password.c_str(), strlen(md5authen)))
	{
		send_BindRsp(Smpp::CommandStatus::MSC_ROK,pdu.sequence_number());

		mStatus=Active;
		//mLiveCounterR=LinkInfo::instance()->enquirelink_counter;
		mLiveCounterT=LinkInfo::instance()->enquirelink_counter;
		mTimer_Bind->stop();
		mTimer_EnquireLink->start(LinkInfo::instance()->interval_enquire_link);
		emit active();
		
	}else
		{
			send_BindRsp(Smpp::CommandStatus::MSC_RINVLOGON,pdu.sequence_number());
		}
}

void SmppServer::send_BindRsp(Smpp::CommandStatus status, Smpp::Uint32 seqnum) {
	
    Smpp::BindRsp pdu;
	pdu.command_status(status);
	pdu.sequence_number(seqnum);
	
    std::cout << "\nSending a BindRsp\n";
    Smpp::Uint8* buff = (Smpp::Uint8*)pdu.encode();
    Smpp::hex_dump(buff , pdu.command_length(), std::cout);
    int bytesSent;
	bytesSent = mDataStreamTrans.writeRawData((const char*)pdu.encode(), pdu.command_length());
}

void SmppServer::send_UnbindReq() {
    Smpp::UnBindReq pdu;
    pdu.sequence_number(++mBaseSeq);
	
    std::cout << "\nsend_UnbindReq\n";
    Smpp::Uint8* d = (Smpp::Uint8*)pdu.encode();
    Smpp::chex_dump(d , pdu.command_length(), stdout);

    int bytesSent;
	bytesSent = mDataStreamTrans.writeRawData((const char*)pdu.encode(), pdu.command_length());
}

// receive the submit sm response
void SmppServer::read_UnbindRsp(Buffer & buf){
    std::cout << "\nread_UnbindRsp\n";
    Smpp::hex_dump(&buf[0], buf.size(), std::cout);
	
    Smpp::UnBindRsp pdu;
    pdu.decode(&buf[0]);

	QString msg;
	msg+="================UnbindRsp================";
	//emit recvmsg(msg);
}


